﻿using System.Drawing;

namespace LeRenard.Libraries.Drawing.GdiPlus.Forms
{
    /// <summary>
    /// Defines a backbuffer to draw frames on.
    /// </summary>
    public sealed class Buffer : IBuffer
    {
        #region Members
        /// <summary>
        /// Internal store of the buffer bitmap to draw on.
        /// </summary>
        private Bitmap _bitmap;

        /// <summary>
        /// Gets the <see cref="Buffer"/>'s width and height in pixels.
        /// </summary>
        public Size Size { get { return _bitmap.Size; } }                

        /// <summary>
        /// Gets the <see cref="Graphics"/> GDI surface to draw on.
        /// </summary>
        public Graphics Graphics
        {
            get {
                if (_graphics == null) {
                    _graphics = Graphics.FromImage(_bitmap);
                }

                return _graphics;
            }
        }
        private Graphics _graphics;
        #endregion

        #region Construction, Deconstruction
        /// <summary>
        /// Creates a new <see cref="Buffer"/> object based on a given <see cref="Size"/>.
        /// </summary>
        /// <param name="size">The size (width and height) of the <see cref="Buffer"/>.</param>
        public Buffer(Size size)
        {
            Initialize(size);
        }

        /// <summary>
        /// Creates a new <see cref="Buffer"/> object based on a given <paramref name="width"/> and <paramref name="height"/>.
        /// </summary>
        /// <param name="width">The width.</param>
        /// <param name="height">The height.</param>
        public Buffer(int width, int height)
            : this(new Size(width, height))
        {
        }

        /// <summary>
        /// Initializes the <see cref="Buffer"/> object.
        /// </summary>
        /// <param name="size">The size (width and height) of the <see cref="Buffer"/>.</param>
        private void Initialize(Size size)
        {
            Deinitialize();

            _bitmap = new Bitmap(size.Width, size.Height);
            _graphics = Graphics.FromImage(_bitmap);
            GraphicsOptimizer.OptimizeBuffer(this);
        }

        /// <summary>
        /// Deinitializes the <see cref="Buffer"/> object.
        /// </summary>
        private void Deinitialize()
        {
            if (_bitmap != null) {
                _bitmap.Dispose();
                _bitmap = null;
            }

            if (_graphics != null) {
                _graphics.Dispose();
                _graphics = null;
            }
        }

        /// <summary>
        /// Releases all resources used by this <see cref="Buffer"/>.
        /// </summary>
        public void Dispose()
        {
            Deinitialize();
        }
        #endregion

        #region Methods
        /// <summary>
        /// Returns the current state of the <see cref="Buffer"/> as a <see cref="Bitmap"/>.
        /// </summary>
        /// <returns>A <see cref="Bitmap"/> representing the current state of the <see cref="Buffer"/>.</returns>
        public Bitmap Render()
        {
            return _bitmap;
        }

        /// <summary>
        /// Clears the <see cref="Buffer"/>'s drawing area to black.
        /// </summary>        
        public void Clear()
        {
            Clear(Color.Black);
        }

        /// <summary>
        /// Clears the <see cref="Buffer"/>'s drawing area to the specified <paramref name="color"/>.
        /// </summary>
        /// <param name="color">The color to use as backdrop when clearing.</param>
        public void Clear(Color color)
        {
            _graphics.Clear(color);
        }

        /// <summary>
        /// Resizes the <see cref="Buffer"/>'s drawing area to the specified <paramref name="size"/>.
        /// </summary>
        /// <param name="size">The new <see cref="Size"/> of the buffer.</param>
        public void Resize(Size size)
        {
            Deinitialize();
            Initialize(size);
        }
        #endregion
    }
}
